package com.ass.netty;

import com.ass.netty.handel.AbstractNettyHandel;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.event.EventListener;

@Slf4j
public abstract class AbstractNetty {


    //获得端口
    protected abstract int getPort();

    //自己的业务处理handler
    protected abstract <Handel extends AbstractNettyHandel> Handel getHandel();

    /**
     * 启动netty 要在spring 容器启动后 进行一些监听 以遍可以从ioc容器中 拿到想要的bean，
     * 否则使用@PostConstruct 注解 因为此注解是在bean初始化的时候执行 有一些业务的bean还没被加载到ioc里面
     * 可能有一些bean 加载不到
     *
     * @param event
     */
    @EventListener(ApplicationPreparedEvent.class)
    public void startNetty(ApplicationPreparedEvent event) {
        int port = getPort();
        new Thread(()->{
            //配置服务端的NIO线程组
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                // 绑定线程池,编码解码
                //服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝
                ServerBootstrap bootstrap = new ServerBootstrap()
                        .group(bossGroup, workerGroup)
                        // 指定Channel
                        .channel(NioServerSocketChannel.class)
                        //使用指定的端口设置套接字地址
                        .localAddress(port)
                        //使用自定义处理类
                        .childHandler(getHandel())
                        //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                        .option(ChannelOption.SO_BACKLOG, 128)
                        //保持长连接，2小时无数据激活心跳机制
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        //将小的数据包包装成更大的帧进行传送，提高网络的负载
                        .childOption(ChannelOption.TCP_NODELAY, true);
                // 绑定端口，开始接收进来的连接
                ChannelFuture future = bootstrap.bind(port).sync();
                if (future.isSuccess()) {
                    log.info("netty服务器开始监听端口：{}",port);
                }
                //关闭channel和块，直到它被关闭
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }).start();
    }





}
